
/*
 * @(#)TimeHandleProgressPriority.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */

package com.dcivision.workflow.core;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionException;

import com.dcivision.alert.bean.MtmUpdateAlertRecipient;
import com.dcivision.alert.bean.UpdateAlert;
import com.dcivision.alert.bean.UpdateAlertType;
import com.dcivision.alert.core.AlertManager;
import com.dcivision.calendar.bean.CalendarRecord;
import com.dcivision.calendar.dao.CalendarRecordDAObject;
import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.DataSourceFactory;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.Utility;
import com.dcivision.framework.notification.EmailDeliveryMessage;
import com.dcivision.framework.notification.NotificationWay;
import com.dcivision.framework.notification.NotificationWayFactory;
import com.dcivision.user.bean.UserRecord;
import com.dcivision.user.dao.UserRecordDAObject;
import com.dcivision.workflow.bean.MtmWfStepNotificationRule;
import com.dcivision.workflow.bean.MtmWorkflowPriorityEscalateLog;
import com.dcivision.workflow.bean.MtmWorkflowProgressPriorityRule;
import com.dcivision.workflow.bean.MtmWorkflowStepPriorityRule;
import com.dcivision.workflow.bean.WorkflowProgress;
import com.dcivision.workflow.bean.WorkflowStep;
import com.dcivision.workflow.dao.MtmWfStepNotificationRuleDAObject;
import com.dcivision.workflow.dao.MtmWfStepNotificationTargetDAObject;
import com.dcivision.workflow.dao.MtmWorkflowPriorityEscalateLogDAObject;
import com.dcivision.workflow.dao.MtmWorkflowProgressPriorityRuleDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepPriorityRuleDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepUserActorDAObject;
import com.dcivision.workflow.dao.WorkflowProgressDAObject;
import com.dcivision.workflow.dao.WorkflowStepDAObject;

/**
 * TimeHandleProgressPriority.java
 * 
 * This class is responsible for handling priority escalation. This class is not
 * supposed to be initialized and used by your own code as it relies on the
 * scheduler. It is scheduled in the accomplishWorkflowProgressByUser method of
 * WorkflowProgressManager
 * 
 * @author Tony Chen
 * @company DCIVision Limited
 * @creation date 26/11/2004
 * @version $Revision: 1.16.2.1 $
 */
public class TimeHandleProgressPriority implements Job {
  
  protected Log log = LogFactory.getLog(this.getClass().getName());
  
  private SessionContainer sessionContainer = null;
  private Connection dbConn = null;
  
  //  private String priorityRuleID = null;
  private String objectType = null;
  private String objectID = null;
  
  /** Creates a new instance of SchedulerTest */
  public TimeHandleProgressPriority() {
    
  }
  /**
   * This is not supposed to be invoked by your own code.
   * 
   * @param context The JobExecutionContext object
   * @throws JobExecutionException
   */
  public void execute(org.quartz.JobExecutionContext context) throws org.quartz.JobExecutionException {
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    String workflowProgressID = dataMap.getString("workflowProgressID");
    this.objectID = dataMap.getString("objectID");
    this.objectType = dataMap.getString("objectType");
    String userRecordID = dataMap.getString("userRecordID");
    
    log.debug("=======workflowProgressID:objectID:objectType:========"+workflowProgressID+":"+objectID+":"+objectType+":"+userRecordID);
    
    log.debug("=====================Reset Progress Priority And Notification Begin.===========================");
    try{
      this.dbConn = DataSourceFactory.getConnection();
      this.sessionContainer = new SessionContainer();
      UserRecordDAObject userDAO = new UserRecordDAObject(sessionContainer, dbConn);
      UserRecord userRecord = (UserRecord)userDAO.getObjectByID(new Integer(userRecordID));
      this.sessionContainer.setUserRecord(userRecord);
      
      resetProgressPriorityAndNotification(new Integer(workflowProgressID));
      this.dbConn.commit();
      
    } catch (ApplicationException appEx) {
      log.error(appEx, appEx);
    } catch (Exception e) {
      log.error(e, e);
    } finally {
      try { this.dbConn.close(); } catch (Exception ignore) {} finally { this.dbConn = null; }
    }
    
    log.debug("=====================Reset Progress Priority And Notification End. ============================");
  }
  
  /**
   * Reset Progress priority and set the notification.
   * 
   * @param workflowProgressID The workflow progress ID
   * @throws ApplicationException
   */
  private void resetProgressPriorityAndNotification(Integer workflowProgressID) throws ApplicationException {
    MtmWorkflowProgressPriorityRuleDAObject  progressPriorityRuleDAO = new MtmWorkflowProgressPriorityRuleDAObject(sessionContainer, dbConn);
    MtmWorkflowStepPriorityRuleDAObject  stepPriorityRuleDAO = new MtmWorkflowStepPriorityRuleDAObject(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionContainer, dbConn);
    UserRecordDAObject userDAO = new UserRecordDAObject(sessionContainer, dbConn);
    MtmWorkflowStepUserActorDAObject userActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    WorkflowProgress wfProgress = (WorkflowProgress)progressDAO.getObjectByID(workflowProgressID);
    WorkflowProgressManager progressManager = new WorkflowProgressManager(sessionContainer, dbConn);
    MtmWfStepNotificationRuleDAObject mtmWfStepNotificationRuleDAO = new MtmWfStepNotificationRuleDAObject(sessionContainer, dbConn);
    MtmWfStepNotificationTargetDAObject mtmWfStepNotificationTargetDAO = new MtmWfStepNotificationTargetDAObject(sessionContainer, dbConn);
    AlertManager am = new AlertManager(sessionContainer, dbConn);
    MailScheduleManager mailManager = new MailScheduleManager(sessionContainer, dbConn);
    
    String progressPriority = wfProgress.getPriority();
    Integer maxPriority = SystemParameterFactory.getSystemParameterInteger(SystemParameterConstant.WORKFLOW_MAX_PRIORITY);
    String oldPriority = wfProgress.getProgressPriority();
    
    if(WorkflowProgress.STATUS_PENDING.equals(wfProgress.getStatus()) ||
        WorkflowProgress.STATUS_IN_PROGRESS.equals(wfProgress.getStatus()) ||
        WorkflowProgress.STATUS_POST_PROGRESS.equals(wfProgress.getStatus())  ){
      
      String notifyType = null;
      String escalateType = null;
      //reset the progress priority.
      if(MtmWorkflowProgressPriorityRule.OBJECT_TYPE_BY_PROGRESS.equals(objectType)){
        MtmWorkflowProgressPriorityRule priorityRule = (MtmWorkflowProgressPriorityRule)progressPriorityRuleDAO.getObjectByID( new Integer(objectID));
        wfProgress = this.resetProgressPriority(wfProgress, priorityRule.getEscalateLevel(),maxPriority,progressDAO);
        notifyType = priorityRule.getEscalateAckMethod();
        escalateType = MtmWorkflowProgressPriorityRule.ESCALATE_TYPE_DYNAMIC;
      } else {
        MtmWorkflowStepPriorityRule stepPriorityRule = (MtmWorkflowStepPriorityRule)stepPriorityRuleDAO.getObjectByID(new Integer(objectID));
        wfProgress = this.resetProgressPriority(wfProgress, stepPriorityRule.getEscalateLevel(),maxPriority,progressDAO);
        notifyType = stepPriorityRule.getEscalateAckMethod();
        escalateType = MtmWorkflowProgressPriorityRule.ESCALATE_TYPE_BASIC;
      }
      
      //record the progress priority escalation history
      this.setPriorityEscalationHistory(wfProgress.getID(), escalateType, new Integer(objectID));
      
      //get the actor for this step.
      WorkflowStep wfStep = (WorkflowStep)stepDAO.getObjectByID(wfProgress.getWorkflowStepID());
      List allNotifyRuleList = mtmWfStepNotificationRuleDAO.getListByWorkflowStepID(wfStep.getID());
      
      log.debug("Notify Type is " + notifyType);
      // Notify by Email & System
      List notifyRuleList = mtmWfStepNotificationRuleDAO.getListByWorkflowStepIDAndNotifyTrigger(wfStep.getID(), MtmWfStepNotificationRule.NOTIFY_TRIGGER_TYPE_PRIORITY_CHANGED);
      if (!Utility.isEmpty(notifyRuleList)) {
        for (int i = 0; i < notifyRuleList.size(); i++) {
          MtmWfStepNotificationRule tmpMtmWfStepNotificationRule = (MtmWfStepNotificationRule) notifyRuleList
              .get(i);

          if (!Utility.isEmpty(tmpMtmWfStepNotificationRule)
              && !Utility.isEmpty(tmpMtmWfStepNotificationRule.getNotifyAckMethod())) {
            String notifyAckMethod = tmpMtmWfStepNotificationRule.getNotifyAckMethod();
            if (notifyType.indexOf(notifyAckMethod) < 0) {
              notifyType += notifyAckMethod;
            }
          }
        }
        List userRecordEmailList = this.getActorUserRecordIDListByWorkflowTrackIDAndRuleList(wfProgress, notifyRuleList);
        for ( int z = 0 ; z < userRecordEmailList.size() ; z++ ) {
          log.debug ( "UserID="+userRecordEmailList.get(z).toString());
        }
        if(!Utility.isEmpty(notifyType) && !Utility.isEmpty(userRecordEmailList)){
          //Notify by Email
          if(notifyType.indexOf(MtmWorkflowProgressPriorityRule.NOTIFY_BY_EMAIL) > -1){
            log.debug("Notify by Email Begin.");
            am.createWorkflowEmailNotification(wfProgress.getID(), UpdateAlert.WORKFLOW_TYPE, UpdateAlert.NOTIFY_TASK_PRIORITY_CHANGED, userRecordEmailList, null, null, new Integer(-1));
            log.debug("Notify by Email End.");
          }
          //Notify by System Log
          if(notifyType.indexOf(MtmWorkflowProgressPriorityRule.NOTIFY_BY_SYSTEM) > -1){
            log.debug(" user record list size: " + userRecordEmailList.size());
            if(!Utility.isEmpty(userRecordEmailList)){
              log.debug("Send system alert Begin.");
              mailManager.sendSystemAlert(wfStep, wfProgress,  UpdateAlert.NOTIFY_TASK_PRIORITY_CHANGED, userRecordEmailList);
              log.debug("Send system alert End.");
            }
          }
        }
      }
    }
  }
  
  
  /**
   * Reset the progress priority for given workfolw progress , escalate level,max priority
   * and workflow progress DAO
   * 
   * @param wfProgress The workflow progress
   * @param escalateLevel The escalate level
   * @param maxPriority The number of max priority
   * @param progressDAO The object of WorkflowProgressDAObject
   * @return object of WorkflowProgress
   * @throws ApplicationException
   * @see com.dcivision.workflow.dao.WorkflowProgressDAObject
   * @see com.dcivision.workflow.bean.WorkflowProgress
   */
  private WorkflowProgress resetProgressPriority(
      WorkflowProgress wfProgress,
      Integer escalateLevel,
      Integer maxPriority,
      WorkflowProgressDAObject progressDAO  )throws ApplicationException{
    String oldProgress = null;
    CalendarRecordDAObject calendarRecordDAO = new CalendarRecordDAObject(sessionContainer, dbConn);
    log.debug(" reset progress priority Begin.");
    int newPriority = 0;
    if(Utility.isEmpty(wfProgress.getProgressPriority())){
      WorkflowStep step = wfProgress.getWorkflowStepSource();
      oldProgress = step.getPriority();
    } else {
      oldProgress = wfProgress.getProgressPriority();
    }
    newPriority = Integer.parseInt(oldProgress) + escalateLevel.intValue();
    if(newPriority < 0){
      newPriority = 0;
    } else if(newPriority > maxPriority.intValue() ){
      newPriority = maxPriority.intValue();
    }
    log.debug("========== Progress Priority Old:EscalateLevel:New=========" + oldProgress + ":" + escalateLevel + ":" + newPriority);
    
    wfProgress.setProgressPriority(String.valueOf(newPriority));
    wfProgress = (WorkflowProgress)progressDAO.updateObject(wfProgress);
    
    // update current progress is calendar Record s
    List calendarRecordList = calendarRecordDAO.getListByObjectIDAndObjectTypeAndStatus(wfProgress.getID(), CalendarRecord.OBJECT_TYPE_WORKFLOW, GlobalConstant.FALSE);
    for (int i = 0; i < calendarRecordList.size(); i++) {
      CalendarRecord calendarRecord = (CalendarRecord)calendarRecordList.get(i);
      calendarRecord.setPriority(wfProgress.getProgressPriority());
      calendarRecordDAO.updateObject(calendarRecord);
    }
    log.debug(" reset progress priority End.");
    return wfProgress;
    
  }
  
  /**
   * Notify by email
   * 
   * @param userRecordIDList
   * @param trackID
   */
  private void notifyByEmail(List userRecordIDList, Integer trackID, String progressPriority) {
    String subject = "";
    String content = "";
    String alertEmail = "";
    
    NotificationWay notifyWay = NotificationWayFactory.getNotificationInstance(NotificationWay.EMAIL_NOTIFICATION_INSTANCE);
    subject = "ParaDM Workflow Task Priority Be Changed Notification";
    content = "It is to notify you that you has assigned the workflow task with the track ID " + trackID
    +", and the task priority was changed. The current task's priority is " + progressPriority +" level.";
    try {
      UserRecordDAObject userDAO = new UserRecordDAObject(this.sessionContainer, this.dbConn);
      for(int i=0; i<userRecordIDList.size(); i++){
        log.debug("user record ID :" + userRecordIDList.get(i) );
        UserRecord userRecord = (UserRecord)userDAO.getObjectByID(new Integer(userRecordIDList.get(i).toString()));
        log.debug("user record name: "+ userRecord.getFullName());
        if(!Utility.isEmpty(userRecord) && !Utility.isEmpty(userRecord.getEmail())){
          alertEmail += userRecord.getEmail() +";";
        }
      }
      
      StringTokenizer st = new StringTokenizer(alertEmail, " ; ", false);
      String sTmp = null;
      while (st.hasMoreTokens()) {
        
        try {
          sTmp = st.nextToken();
          EmailDeliveryMessage mesg = new EmailDeliveryMessage();
          String smtpAdminAddress = SystemParameterFactory.getSystemParameter(SystemParameterConstant.SMTP_ADMIN_ADDRESS);
          String smtpAdminName = SystemParameterFactory.getSystemParameter(SystemParameterConstant.SMTP_ADMIN_NAME);
          mesg.setSender(new com.dcivision.framework.bean.GenericAddress(smtpAdminAddress, smtpAdminName, "UTF-8"));
          mesg.addToRecipient(new com.dcivision.framework.bean.GenericAddress(sTmp.trim(), sTmp.trim(), "UTF-8"));
          mesg.setSubject(subject);
          mesg.setMailContent(content);
          
          notifyWay.addDeliveryMessage(mesg);
          notifyWay.send();
        } catch (Exception e) {
          log.error("Bad Email address:" + sTmp + ",Ignored");
        }
      }
      
    } catch (Exception e) {
      log.error(e, e);
      log.error("error in add recipient");
    }
  }
  
  /**
   * Send system alert
   * 
   * @param wstep
   * @param wprogress
   * @param alertActionType
   * @param userRecordList
   * @throws ApplicationException
   */
  private void sendSystemAlert(WorkflowStep wstep, WorkflowProgress wprogress, String alertActionType, List userRecordIDList) throws ApplicationException {
    // system log
    log.debug("start adding alert");
    SessionContainer sessionCon = this.sessionContainer;
    //    if(Utility.isEmpty(dbConn)){
    //        try {
    //            dbConn = DataSourceFactory.getConnection();
    //        } catch (Exception ex){
    //          throw new ApplicationException(ex);
    //        }
    //    }
    AlertManager alertManager = new AlertManager(sessionCon, dbConn);
    UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCon, dbConn);
    UserRecord userRecord = (UserRecord) userRecordDAO.getObjectByID(wprogress.getCreatorID());
    sessionCon.setUserRecord(userRecord);
    
    log.debug("start adding alert");
    UpdateAlert updateAlert = new UpdateAlert();
    updateAlert.setMessage(null);
    updateAlert.setObjectType(GlobalConstant.OBJECT_TYPE_WORKFLOW);
    updateAlert.setObjectID(wprogress.getID());
    updateAlert.setDueDate(null);
    
    log.debug("start adding alert type");
    UpdateAlertType[] updateAlertTypes = new UpdateAlertType[1];
    UpdateAlertType updateAlertType = new UpdateAlertType();
    updateAlertType.setUpdateAlertID(updateAlert.getID());
    updateAlertType.setActionType(alertActionType);
    updateAlertType.setNotifyWay(UpdateAlert.SYSTEM_NOTIFICATION);
    updateAlertType.setMessage(updateAlert.getMessage());
    updateAlertType.setDueDate(updateAlert.getDueDate());
    updateAlertTypes[0] = updateAlertType;
    
    MtmUpdateAlertRecipient[] updateRecipient = new MtmUpdateAlertRecipient[1];
    log.debug("start adding Update alert receipent");
    updateRecipient = alertManager.getRecipient(Utility.getStringArray(userRecordIDList), new String[0], new String[0]);
    // List notifyUserIDList = alertManager.getDistinctUserIDListByLists(Utility.getStringArray(userRecordList), new String[0], new String[0]);
    updateAlert =alertManager.createUpdateAlert(updateAlert, updateAlertTypes, updateRecipient, null, "", null, "", sessionCon.getUserRecordID(), userRecordIDList);
  }
  
  /**
   * Record this progress priority escalate history.
   * 
   * @param progressID
   * @param escalateType
   * @param escalateRuleID
   * @throws ApplicationException
   */
  private void setPriorityEscalationHistory(Integer progressID,String escalateType, Integer escalateRuleID) throws ApplicationException{
    MtmWorkflowPriorityEscalateLogDAObject historyDAO = new MtmWorkflowPriorityEscalateLogDAObject(this.sessionContainer,this.dbConn);
    
    log.debug(" Record this progress priority escalate history ----- Begin.");
    //Record this progress priority escalate history.
    MtmWorkflowPriorityEscalateLog history = new MtmWorkflowPriorityEscalateLog();
    history.setWorkflowProgressID(progressID);
    history.setEscalateType(escalateType);
    history.setEscalateRuleID(escalateRuleID);
    historyDAO.insertObject(history);
    log.debug(" Record this progress priority escalate history ----- End.");
  }
  /**
   * Gets a list of actor user record ID by workflow track and rule list
   * for given workflow track and rele list
   * 
   * @param wfProgress The workflow
   * @param ruleList
   * @return list of actior user record ID
   * @throws ApplicationException
   */
  private List getActorUserRecordIDListByWorkflowTrackIDAndRuleList(WorkflowProgress wfProgress, List ruleList) throws ApplicationException{
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionContainer, dbConn);
    MtmWfStepNotificationTargetDAObject mtmWfStepNotificationTargetDAO = new MtmWfStepNotificationTargetDAObject(sessionContainer, dbConn);
    MailScheduleManager mailScheduleManager = new MailScheduleManager(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    List userRecordIDList = new ArrayList();
    try {
      if (!Utility.isEmpty(ruleList)) {
        for (int i = 0; i < ruleList.size(); i++) {
          MtmWfStepNotificationRule mtmWfStepNotificationRule = (MtmWfStepNotificationRule)ruleList.get(i);
          List recipientList = new ArrayList();
          recipientList = mailScheduleManager.getAllActorEmailInfoListByWorkflowProgressAndRuleID(wfProgress, mtmWfStepNotificationRule.getID(), false);
          Iterator iterator = recipientList.iterator();
          while (iterator.hasNext()) {
            userRecordIDList.add(iterator.next());
            log.debug("The userRecordList is.....Where is At Owner ::" + userRecordIDList.size());
          }
        }
      }  
    } catch (ApplicationException ex) {
      log.error(ex, ex);
    }
    return (userRecordIDList);
  }
  
}

